/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.                            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *****************************************************************************/
package picounit.features.test_finder;

import picounit.PicoUnitException;
import picounit.features.classloader.ClassReloader;
import picounit.features.test_instantiator.Instantiator;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;

public class ObjectReloader {
	private final Object deserializer;
	
	public ObjectReloader(ClassReloader classReloader) {
		this(new ClassReloadingInstantiator(classReloader, new Instantiator() {
			public <T> T instantiate(Class<T> toInstantiate) {
				try {
					return toInstantiate.newInstance();
				}
				catch (InstantiationException e) {
					throw new PicoUnitException(e);
				}
				catch (IllegalAccessException e) {
					throw new PicoUnitException(e);
				}
			}
		}));
	}

	public ObjectReloader(Instantiator instantiator) {
		this.deserializer = instantiator.instantiate(Deserializer.class);
	}

	@SuppressWarnings("unchecked")
	public <T extends Serializable> T reload(T object) {
		try {
			return (T) deserialize(serialize(object));
		}
		catch (IOException e) {
			throw new PicoUnitException("Could not reload: " + object, e);
		}
		catch (Exception e) {
			throw new PicoUnitException(e);
		}
	}

	private Object deserialize(byte[] byteArray) throws IllegalArgumentException, SecurityException,
		IllegalAccessException, InvocationTargetException, NoSuchMethodException {

		return deserializer.getClass().getMethod("deserialize", byte[].class)
			.invoke(deserializer, byteArray);
	}

	private <T> byte[] serialize(T object) throws IOException {
		final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
		
		objectOutputStream.writeObject(object);
		
		byte[] byteArray = byteArrayOutputStream.toByteArray();
		return byteArray;
	}
	
	public static class Deserializer {
		public Object deserialize(byte[] bytes) {
			ObjectInputStream objectInputStream;
			try {
				objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes));
				
				return objectInputStream.readObject();
			}
			catch (IOException e) {
				e.printStackTrace();
			}
			catch (ClassNotFoundException e) {
				e.printStackTrace();
			}

			return null;
		}
	}
}